﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;

namespace DateTimeAccurate
{
    class DateTimeAccurate
    {
        public static DateTimeAccurate Now = new DateTimeAccurate(10 * 1000);
        private static long _resolution;
        private long ticks;
        private Stopwatch sw;
        private bool stopped = false;
        private long synchronizationPeriod;
        private object lockobj = new object();

        public DateTimeAccurate(long synchronizationPeriod)
        {
            this.synchronizationPeriod = synchronizationPeriod;
            sw = new Stopwatch();
            sw.Start();
            FindResolution();
            Synchronize();
        }

        ~DateTimeAccurate()
        {
            stopped = true;
            sw.Stop();
            sw = null;
        }

        public void Profile()
        {
            StringBuilder sb = new StringBuilder();
            Stopwatch sw = new Stopwatch();
            DateTimeAccurate.Now.StartSynchronize();
            long ticks = DateTime.Now.Ticks;
            long elapsedTime0 = ticks;
            long elapsedTime1 = ticks;
            sw.Start();
            int num = 10000;
            while (num > 0)
            {
                if (ticks != DateTime.Now.Ticks)
                {
                    elapsedTime0 = DateTime.Now.Ticks - ticks;
                    elapsedTime1 = sw.Elapsed.Ticks;
                    ticks = DateTime.Now.Ticks;
                    sw.Reset();
                    sw.Start();
                    num--;
                    sb.AppendLine(elapsedTime0 + "\t" + elapsedTime1);
                }
                else
                {
                }
            }
            File.WriteAllText("temp.log", sb.ToString());
            DateTimeAccurate.Now.StopSynchronize();
        }

        private void FindResolution()
        {
            long ticks = DateTime.Now.Ticks;
            while (true)
            {
                if (ticks != DateTime.Now.Ticks)
                {
                    _resolution = DateTime.Now.Ticks - ticks;
                    break;
                }
                else
                {
                }
            }
        }

        public void StopSynchronize()
        {
            stopped = true;
        }

        public void StartSynchronize()
        {
            stopped = false;
            Thread t = new Thread(SynchronizeThread);
            t.Start();
        }

        public DateTime CurDateTime()
        {
            lock (lockobj)
            {
                return new DateTime(ticks + sw.Elapsed.Ticks);
            }
        }

        public long Ticks
        {
            get
            {
                lock (lockobj)
                {
                    return ticks + sw.Elapsed.Ticks;
                }
            }
        }

        public void SynchronizeThread()
        {
            while (stopped == false)
            {
                Synchronize();
            }
        }

        public void Synchronize()
        {
            long ticks = DateTime.Now.Ticks;
            while (true)
            {
                if (ticks != DateTime.Now.Ticks)
                {
                    ticks = DateTime.Now.Ticks;
                    if (this.ticks + sw.Elapsed.Ticks < ticks - _resolution)
                    {
                        lock (lockobj)
                        {
                            this.ticks = ticks - _resolution;
                            sw.Reset();
                            sw.Start();
                            break;
                        }
                    }
                    if (this.ticks + sw.Elapsed.Ticks > ticks + _resolution)
                    {
                        lock (lockobj)
                        {
                            this.ticks = ticks + _resolution;
                            sw.Reset();
                            sw.Start();
                            break;
                        }
                    }
                }
                else
                {
                }
            }
        }

    }
}